package com.itdaan.portal.business.controller;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.DefaultHashService;
import org.apache.shiro.crypto.hash.Hash;
import org.apache.shiro.crypto.hash.HashRequest;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.itdaan.portal.business.domain.Permission;
import com.itdaan.portal.business.domain.Role;
import com.itdaan.portal.business.domain.User;
import com.itdaan.portal.business.service.PermissionService;
import com.itdaan.portal.business.service.RoleService;
import com.itdaan.portal.business.service.UserService;
import com.itdaan.portal.common.exception.ServiceException;
import com.itdaan.portal.common.util.ApplicationProperty;

/**
 * Shiro管理对象
 * @author www.itdaan.com
 */
public class ShiroDbRealm extends AuthorizingRealm {

	private Logger logger = LoggerFactory.getLogger(ShiroDbRealm.class);
	
	@Autowired
	private UserService userService;
	@Autowired
	private RoleService roleService;
	@Autowired
	private PermissionService permissionService;
	
	/**
	 * 认证回调函数,登录时调用.
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		User user = null;
		try {
			user = userService.getByUserName(token.getUsername());
		} catch (ServiceException e) {
			e.printStackTrace();
		}
		if (user != null) {
			return new SimpleAuthenticationInfo(new ShiroUser(user.getId(),
					user.getUsername(), user.getName(), user.getSource() ,user.getAvatar()),
					user.getPassword(), getName());
		} else {
			return null;
		}
	}

	/**
	 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
		
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		// 加载用户的roles
		List<Role> roles = new ArrayList<Role>();
		try {
			roles = roleService.getByUserId(shiroUser.id);
		} catch (ServiceException e) {
			logger.error("查询角色异常：{}",e.toString());
			e.printStackTrace();
		}
		List<String> stringRoles = new ArrayList<String>(roles.size());
		for (Role role : roles) {
			stringRoles.add(role.getName());
		}
		info.addRoles(stringRoles);
		
		// 加载用户的permissions
		List<Permission> permissions = new ArrayList<Permission>();
		try {
			permissions = permissionService.getByUserId(shiroUser.id);
		} catch (ServiceException e) {
			logger.error("查询权限异常：{}",e.toString());
			e.printStackTrace();
		}
		Set<String> stringPermissions = new HashSet<String>(permissions.size());
		for (Permission permission : permissions) {
			stringPermissions.add(permission.getValue());
		}
		info.setStringPermissions(stringPermissions);
		
		return info;
	}

	/**
	 * 生成散列密码，采用spring/shiro.xml配置的credentialsMatcher加密算法 。<br/>
	 * 
	 * 目前采用SHA-256加密算法，以Base64方式存储，加密迭代次数1024；<br/>
	 * SHA-256散列的Base64存储需占数据库(44位字符)，而Hex存储需占数据库（64位字符）；<br/>
	 * 注意SHA-384、SHA-512哈希加密需占更长字节存储（打印出来可以数一下字符长度）
	 * 
	 * @author Junas.Cheung
	 */
    public String encrytPassword(String password) {
        // 获取凭证子类配置
        String  hashAlgorithmName = ApplicationProperty.getProperty("password.hash.algorithm.name");
        int     iterations        = Integer.parseInt(ApplicationProperty.getProperty("password.iterations"));
        boolean isStoredHex       = Boolean.parseBoolean(ApplicationProperty.getProperty("password.is.stored.hex"));
        // 散列
        DefaultHashService hashService = new DefaultHashService();
        // 设置算法
        hashService.setHashAlgorithmName(hashAlgorithmName);
        // 迭代次数
        hashService.setHashIterations(iterations);
        // 获取字节
        ByteSource byteSource = ByteSource.Util.bytes(password);
        Hash hash = hashService.computeHash(
             new HashRequest.Builder().setSource(byteSource).build());
        // 生成Hash值
        return isStoredHex ? hash.toHex() : hash.toBase64();
    }

	/**
	 * 自定义Authentication对象，使得Subject除了携带用户的登录名外还可以携带更多信息.
	 */
	public static class ShiroUser implements Serializable {
		private static final long serialVersionUID = -1373760761780840081L;
		private int id;
		private String username;
		private String name;
		private int source;
		private String avatar;

		public ShiroUser(int id, String username, String name, int source,String avatar) {
			this.id = id;
			this.username = username;
			this.name = name;
			this.source = source;
			this.avatar = avatar;
		}

		public String getName() {
			return name;
		}

		public String getUsername() {
			return username;
		}
		
		public int getId() {
			return id;
		}

		public void setId(int id) {
			this.id = id;
		}

		public void setUsername(String username) {
			this.username = username;
		}

		public void setName(String name) {
			this.name = name;
		}

		public int getSource() {
			return source;
		}

		public void setSource(int source) {
			this.source = source;
		}

		public String getAvatar() {
			return avatar;
		}

		public void setAvatar(String avatar) {
			this.avatar = avatar;
		}

		/**
		 * 本函数输出将作为默认的<shiro:principal/>输出.
		 */
		@Override
		public String toString() {
			return name;
		}
	}
	
}
